/**********************************************************************************************************************
 *
 * Project:    mGameEngine
 * File:       scenenode.h
 * Author:     karooolek
 * Created on: 2009-03-10
 *
 **********************************************************************************************************************/

#ifndef SCENENODE_H_
#define SCENENODE_H_

#include "../common.h"
#include "../graphics.h"
#include "../audio.h"
#include "../physics.h"

namespace mGameEngine
{
namespace Game
{

// prototype
class Scene;

/**
 * Basic element of game scene.
 * Game scene nodes are organized in tree hierarchy.
 * Each node has its position and orientation relative to its parent.
 * Graphics scene nodes have cameras, lights and drawable elements attached.
 * Audio scene nodes have microphones and sound sources.
 * Physics scene nodes have collision volume, physical properties and
 * force fields.
 */
class SceneNode
{
    friend class Scene;

protected:
    /**
     * Name.
     */
    String _name;

    /**
     * User-defined flags.
     */
    uint32 _flags;

    /**
     * Scene containing scene node.
     */
    mutable Scene *_scene;

    /**
     * Parent scene node.
     */
    SceneNode *_parent;

    /**
     * Sub-nodes, children.
     */
    List<SceneNode *> _nodes;

    /**
     * Local transformation.
     */
    Matrix4x4 _transformation;

    /**
     * Camera attached to scene node.
     */
    Graphics::Camera *_camera;

    /**
     * Lights attached to scene node.
     */
    List<Graphics::Light *> _lights;

    /**
     * Drawable elements attached to scene node.
     */
    List<Graphics::Drawable *> _drawables;

    /**
     * Microphone attached to scene node.
     */
    Audio::Microphone *_microphone;

    /**
     * Sound sources attached to scene node.
     */
    List<Audio::SoundSource *> _soundSources;

    /**
     * Physics actor attached to scene node.
     */
    Physics::Actor *_actor;

    /**
     * Set recursively scene containing scene node.
     * @param scene scene containing scene node.
     */
    void _setScene(Scene *scene);

public:
    /**
     * Create new scene node with specified name.
     * @param name scene node name.
     */
    SceneNode(const String &name = String());

    /**
     * Create new scene node with specific name from mesh.
     * @param mesh mesh
     * @param name scene node name.
     */
    SceneNode(Graphics::Mesh *mesh, const String &name = String());

    /**
     * Destroy scene node.
     */
    virtual ~SceneNode();

    /**
     * Get scene node name.
     * @return scene node name.
     */
    const String &getName() const
    {
        return _name;
    }

    /**
     * Set scene node name.
     * @param name name.
     */
    void setName(const String &name)
    {
        _name = name;
    }

    /**
     * Get scene node flags.
     * @return scene node flags.
     */
    uint32 getFlags() const
    {
        return _flags;
    }

    /**
     * Set scene node flags.
     * @param flags flags to set.
     */
    void setFlags(uint32 flags)
    {
        _flags = flags;
    }

    /**
     * Check if specified flags are raised.
     * @param flags flags.
     * @return true if all specified flags are set, false otherwise.
     */
    bool areFlagsRaised(uint32 flags)
    {
        return (_flags & flags) == flags;
    }

    /**
     * Raise scene node specified flags.
     * @param flags flags to raise.
     */
    void raiseFlags(uint32 flags)
    {
        _flags |= flags;
    }

    /**
     * Clear scene node specified flags.
     * @param flags flags to clear.
     */
    void clearFlags(uint32 flags)
    {
        _flags &= ~flags;
    }

    /**
     * Get scene containing scene node.
     * @return scene containing scene node.
     */
    Scene *getScene() const;

    /**
     * Get parent scene node.
     * @return parent scene node.
     */
    SceneNode *getParentSceneNode() const
    {
        return _parent;
    }

    /**
     * Get root scene node.
     * @return root scene node.
     */
    virtual SceneNode *getRootSceneNode() const;

    /**
     * Get children scene nodes.
     * @return list of children scene nodes.
     */
    const List<SceneNode *> &getSceneNodes() const
    {
        return _nodes;
    }

    /**
     * Add new child scene node.
     * @param node new child scene node
     */
    virtual void addSceneNode(SceneNode *node);

    /**
     * Remove child scene node.
     * @param node child scene node to be removed
     */
    virtual void removeSceneNode(SceneNode *node);

    /**
     * Check if scene node has child scene node with specified name.
     * @param name scene node name.
     * @return true if scene node has child scene node with
     * specified name, false otherwise.
     */
    virtual bool hasSceneNode(const String &name) const;

    /**
     * Get child scene node with specified name.
     * @param name scene node name.
     * @return child scene node with specified name or NULL.
     */
    virtual SceneNode *getSceneNode(const String &name) const;

    /**
     * Get scene node position.
     * @param relative relative scene node. Default relative is nodes parent.
     * @return scene node position relating to relative scene node
     */
    virtual Vector3 getPosition(const SceneNode *relative = NULL) const;

    /**
     * Set scene node position.
     * @param position scene node position relating to relative scene node.
     * @param relative relative scene node. Default relative is nodes parent.
     */
    virtual void setPosition(const Vector3 &position, const SceneNode *relative = NULL);

    /**
     * Move scene node.
     * @param move scene node movement relating to relative scene node.
     * @param relative relative scene node Default relative is nodes parent.
     */
    virtual void move(const Vector3 &move, const SceneNode *relative = NULL);

    /**
     * Get scene node orientation.
     * @param relative relative scene node. Default relative is nodes parent.
     * @return scene node orientation relating to relative scene node.
     */
    virtual Quaternion getOrientation(const SceneNode *relative = NULL) const;

    /**
     * Set scene node orientation.
     * @param orientation scene node orientation relating to relative scene node.
     * @param relative relative scene node. Default relative is nodes parent.
     */
    virtual void setOrientation(const Quaternion &orientation, const SceneNode *relative = NULL);

    /**
     * Rotate scene node.
     * @param rotate scene node rotation relating to relative scene node.
     * @param relative relative scene node Default relative is nodes parent.
     */
    virtual void rotate(const Quaternion &rotate, const SceneNode *relative = NULL);

    /**
     * Get scene node relative transformation.
     * @param relative relative scene node. NULL for nodes parent.
     * @return scene node transformation relative to relative scene node.
     */
    virtual Matrix4x4 getTransformation(const SceneNode *relative = NULL) const;

    /**
     * Set scene node relative transformation.
     * @param transformation transformation.
     * @param relative relative scene node. NULL for nodes parent.
     */
    virtual void setTransformation(const Matrix4x4 &transformation,
                            const SceneNode *relative = NULL);

    /**
     * Get camera attached to scene node.
     * @return camera attached to scene node.
     */
    Graphics::Camera *getCamera() const
    {
        return _camera;
    }

    /**
     * Attach camera to scene node.
     * @param camera camera to attach.
     */
    void setCamera(Graphics::Camera *camera)
    {
        _camera = camera;
    }

    /**
     * Get lights attached to scene node.
     * @return lights attached to scene node.
     */
    const List<Graphics::Light *> &getLights() const
    {
        return _lights;
    }

    /**
     * Attach light to scene node.
     * @param light light to attach.
     */
    virtual void addLight(Graphics::Light *light);

    /**
     * Detach light from scene node.
     * @param light light to detach.
     */
    virtual void removeLight(Graphics::Light *light);

    /**
     * Get drawable elements attached to scene node.
     * @return drawable elements attached to scene node.
     */
    const List<Graphics::Drawable *> &getDrawables() const
    {
        return _drawables;
    }

    /**
     * Attach drawable element to scene node.
     * @param drawable drawable element to attach
     */
    virtual void addDrawable(Graphics::Drawable *drawable);

    /**
     * Detach drawable element from scene node.
     * @param drawable drawable element to detach.
     */
    virtual void removeDrawable(Graphics::Drawable *drawable);

    /**
     * Get microphone attached to scene node.
     * @return microphone attached to scene node.
     */
    Audio::Microphone *getMicrophone() const
    {
        return _microphone;
    }

    /**
     * Attach microphone to scene node.
     * @param microphone microphone to attach.
     */
    void setMicrophone(Audio::Microphone *microphone)
    {
        _microphone = microphone;
    }

    /**
     * Get sound sources attached to scene node.
     * @return sound sources attached to scene node
     */
    const List<Audio::SoundSource *> &getSoundSources() const
    {
        return _soundSources;
    }

    /**
     * Attach sound source to scene node.
     * @param soundSource sound source to attach.
     */
    virtual void addSoundSource(Audio::SoundSource *soundSource);

    /**
     * Detach sound source from scene node.
     * @param soundSource sound source to detach.
     */
    virtual void removeSoundSource(Audio::SoundSource *soundSource);

    /**
     * Get physics actor attached to scene node.
     * @return physics actor attached to scene node.
     */
    Physics::Actor *getActor() const
    {
        return _actor;
    }

    /**
     * Attach physics actor to scene node.
     * @param actor physics actor.
     */
    void setActor(Physics::Actor *actor)
    {
        _actor = actor;
    }

    /**
     * Scene node idle loop.
     */
    virtual void idle();

    /**
     * Scene node update loop.
     * @param dt update time difference in seconds.
     */
    virtual void update(float dt);
};

}
}

#endif // SCENENODE_H_
